#ifndef TRANSFORMERCONTENTPROTOCOL_HPP_3
#define TRANSFORMERCONTENTPROTOCOL_HPP_3

#include <ctype.h>
#include "transContentDefine.h"
#include "transContentAssistent.hpp"
#include "transContentErrorCode.h"
#include "logger.hpp"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// data content protocol
///
///	data profile
///	  name: |total legnth|message type|local Port|target port|local address|flag|flag value|data length|data|
/// length: |    2       |     1      |    4     |     4     |   8         | 1  |   4      |   2       |  xx|
/// offset: |    0       |     2      |    3     |     7     |   11        |19  |   20     |   24      | 26 |
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
class TransContentProtocol{
	public:
		static int HeaderLengthOut;
		static int HeaderLengthIn;
	public:
		static int getHeaderLengthOut(){
			return HeaderLengthOut;
		}

		static int getHeaderLengthIn(){
			return HeaderLengthIn;
		}

		static void setPacketTotalLength(char* content, int length){
			*((short*)(content)) = (short)length;
		}
		static int getPacketTotalLength(const char* content, int towards){

			return towards==DUMP_PROTOCOL_PACKET_OUT ? *((short*)(content)): E_TRANC_ParamError;
		}

		static void setPacketContentType(char* content, int type){
			content[2] = (char)type;
		}
		static int getPacketContentType(const char* content, int towards){
			return towards==DUMP_PROTOCOL_PACKET_OUT ? content[2] : content[0];
		}

		static void setPacketLocalPort(char* content, int localPort){
			*((int*)(content+3)) = TransContentAssis::INTL2N(localPort);
		}
		static int getPacketLocalPort(const char* content, int towards){
			return towards==DUMP_PROTOCOL_PACKET_OUT ? TransContentAssis::INTN2L(*((int*)(content+3))) : TransContentAssis::INTN2L(*((int*)(content+1)));
		}

		static void setPacketTargetPort(char* content, int targetPort){
			*((int*)(content+7)) = TransContentAssis::INTL2N(targetPort);
		}

		static int getPacketTargetPort(const char* content, int towards){
			return towards==DUMP_PROTOCOL_PACKET_OUT ? TransContentAssis::INTN2L(*((int*)(content+7))) : TransContentAssis::INTN2L(*((int*)(content+5)));
		}

		static void setPacketSourceAddr(char* content, const NodeAddr* source){
			NodeAddr _tempLocal = *source;
			TransContentAssis::NodeAddrL2N(&_tempLocal);
			*((long long int*)(content + 11)) = _tempLocal.serial_id;
		}

		static NodeAddr getPacketSourceAddr(const char* content, int towards){
			NodeAddr _tempSource;
		   _tempSource.serial_id = towards==DUMP_PROTOCOL_PACKET_OUT ? *((long long int*)(content + 11)):*((long long int*)(content + 9));
			TransContentAssis::NodeAddrN2L(&_tempSource);
			return _tempSource;
		}

		static void setPacketHeaderFlag(char* content, int flag){
			content[19] = (char)flag;
		}

		static int getPacketHeaderFlag(const char* content, int towards){
			return towards==DUMP_PROTOCOL_PACKET_OUT ? content[19]:content[17];
		}

		static void setPacketHeaderFlagValue(char* content, int flagValue){
			int _tempFlagValue = TransContentAssis::INTL2N(flagValue);
			*((int*)(content+20)) = _tempFlagValue;
		}

		static int getPacketHeaderFlagValue(const char* content, int towards){
			int _tempFlagValue = towards==DUMP_PROTOCOL_PACKET_OUT ? *((int*)(content+20)):*((int*)(content+18));
			return TransContentAssis::INTN2L(_tempFlagValue);
		}

		static void setPacketDataLength(char* content, int length){
			short _tempLength = TransContentAssis::SHORTL2N((short)length);
			*((short*)(content+24)) = _tempLength;
		}

		static int getPacketDataLength(const char* content, int towards){
			short _tempLength = towards==DUMP_PROTOCOL_PACKET_OUT ? *((short*)(content+24)):*((short*)(content+22));
			return TransContentAssis::SHORTN2L(_tempLength);
		}

		static char* getPacketDataPart(char* content, int towards){
			return towards==DUMP_PROTOCOL_PACKET_OUT ? content+HeaderLengthOut:content+HeaderLengthIn;
		}

		static int appendPacketData(char* content, const char* data, int length){
			memcpy(content+HeaderLengthOut, data, length);
		}

		static int dumpPacketHeader(const char* content, const char* fileName, int towards){
			if(content == NULL || towards != DUMP_PROTOCOL_PACKET_OUT || towards != DUMP_PROTOCOL_PACKET_IN){
				log_error("parameter content or towrad for dump header error");
				return E_TRANC_ParamError;
			}
			if((towards == DUMP_PROTOCOL_PACKET_OUT) && (strlen(content) < HeaderLengthOut)){
				log_error("parameter content length for dump header out error");
				return E_TRANC_ParamError;
			}else if((towards == DUMP_PROTOCOL_PACKET_IN) && (strlen(content) < HeaderLengthIn)){
				log_error("parameter content length for dump header in error");
				return E_TRANC_ParamError;
			}

			FILE* fd = fopen(fileName, "a+");
			if(fd == NULL){
				log_error("open file error: %s", strerror(errno));
				return E_TRANC_SystemError;
			}
			//write content
			fprintf(fd, "dump packet header:\n");
			if(towards == DUMP_PROTOCOL_PACKET_OUT){
				fprintf(fd, "\rpacket total length: %d\n", getPacketTotalLength(content, towards));
			}
			fprintf(fd, "\rpacket content type: %d\n", getPacketContentType(content, towards));
			fprintf(fd, "\rpacket local port: %d\n", getPacketLocalPort(content, towards));
			fprintf(fd, "\rpacket target port: %d\n", getPacketTargetPort(content, towards));
			fprintf(fd, "\rpacket sourceAddr: %lld\n", getPacketSourceAddr(content, towards).serial_id);
			fprintf(fd, "\rpacket header flag: %d\n", getPacketHeaderFlag(content, towards));
			fprintf(fd, "\rpacket header flag value: %d\n", getPacketHeaderFlagValue(content, towards));
			fprintf(fd, "\rpacket data length: %d\n", getPacketDataLength(content, towards));
			return E_TRANC_SUCESS;
		}

		static int dumpPacketBody(char* content, const char* fileName, int towards){
			const char* dataBody = getPacketDataPart(content, towards);
			int length = getPacketDataLength(content, towards);
			int count = 0, j=0;

			FILE* fd = fopen(fileName, "a+");
			if(fd == NULL){
				log_error("open %s to append failed; %s", fileName, strerror(errno));
				return E_TRANC_SystemError;
			}
			fprintf(fd, "\r content: \n");
			for(int i=0; i< length; i+=16){
				
				for(j=i, count=0; count < 16 && j< length; ++count, ++j){
					fprintf(fd, "%02X ", dataBody[j]);
				}

				while(count < 16){
					fprintf(fd, "   ");
					++count;
				}

				for(j=i, count=0; count < 16 && j<length; ++count, ++j){
					if(isalnum(dataBody[j])){
						fprintf(fd, "%c", dataBody[j]);
					}else{
						fprintf(fd, ".");
					}
				}
				fprintf(fd, "\n");
			}
			return E_TRANC_SUCESS;
		}

		static int dumpPacketWhole(char* content, const char* fileName, int towards){
			int result = dumpPacketHeader(content, fileName, towards);
			if(result != E_TRANC_SUCESS){
				return result;
			}
			return dumpPacketBody(content, fileName, towards);
		}

};
int TransContentProtocol::HeaderLengthOut = 26;
int TransContentProtocol::HeaderLengthIn = 24;
#endif //TRANSFORMERCONTENTPROTOCOL_HPP_3
